import type { Ref } from 'vue'

type GetDataByRequest<T> = T extends (...args: any[]) => Promise<infer R>
  ? R extends { list?: infer D }
    ? D
    : never
  : never

type PromiseFn = (...args: any[]) => Promise<any>

type Pagination = {
  page: number
  limit: number
  offset: number
  total: number
}

type DefaultParams = Partial<Pagination> & Record<string, any>

type RunParams<T extends PromiseFn, P> =
  | (Parameters<T>[0] & Partial<P>)
  | Parameters<T>[0]

/**
 * 通用list请求函数
 * @param fn 请求函数
 * @param defaultParams 默认可选参数
 * @returns
 */
export function useListRequest<T extends PromiseFn, D extends DefaultParams>(
  fn: T,
  defaultParams?: D,
  loadMore?: boolean
) {
  const loading = ref(false)
  const disabled = ref(false)
  const data = ref([]) as Ref<GetDataByRequest<T>>
  const pagination = reactive<Pagination>({
    page: 1,
    limit: 20,
    offset: 0,
    total: 0
  })

  watch(
    () => pagination.page,
    page => {
      pagination.offset = (page - 1) * pagination.limit
    },
    { immediate: true }
  )

  const run = async (params?: RunParams<T, D>, ...args: any[]) => {
    loading.value = true
    // disabled.value = true
    try {
      await nextTick()
      await fn(
        {
          ...defaultParams,
          offset: pagination.offset,
          limit: pagination.limit,
          ...params
        },
        ...args
      ).then(res => {
        if (!loadMore) {
          data.value = res.list
        } else {
          // if (!res?.list || res.list.length < pagination.limit) {
          //   disabled.value = true
          // }
          data.value = data.value.concat(res.list)
        }
        pagination.total = res.total
      })
      // .finally(() => (disabled.value = false))
    } finally {
      loading.value = false
    }
  }

  return { loading, run, data, pagination, disabled }
}

type GetSwitchData<K, T> = T extends (
  | [K, PromiseFn]
  | [K, PromiseFn, DefaultParams]
)[]
  ? GetDataByRequest<T[number][1]>
  : never

type GetSwitchRun<K, T> = T extends (
  | [K, infer R extends PromiseFn]
  | [K, infer R extends PromiseFn, infer D extends DefaultParams]
)[]
  ? (params?: RunParams<R, D>, ...args: any[]) => Promise<void>
  : never

type SwitchFnList<T> = DeepReadonly<
  ([T, PromiseFn, DefaultParams] | [T, PromiseFn])[]
>

/**
 * 多个请求函数切换
 * @param active 切换的值
 * @param switchFnList 切换的函数列表,参数后加 as const, [active, fn][] | [active, fn, defaultParams][]
 * @returns
 * @example
 * ```ts
 * const request = useSwitchRequest(active, [
 *   [active1, fn1, state1],
 *   [active2, fn2, state2],
 * ])
 * ```
 */
export function useSwitchRequest<T, S extends SwitchFnList<T>>(
  active: Ref<T>,
  switchFnList: S
) {
  const requestList = switchFnList.map(([a, b, c]) => [
    a,
    useListRequest(b, c)
  ]) as [keyof T, ReturnType<typeof useListRequest>][]

  const request = computed(() => {
    const item = requestList.find(([a]) => a === active.value)!
    return item[1]
  })

  const loading = computed(() => request.value.loading.value)
  const data = computed(
    () => request.value.data.value as unknown as GetSwitchData<T, NoReadonly<S>>
  )

  const run = computed(
    () => request.value.run as unknown as GetSwitchRun<T, NoReadonly<S>>
  )

  const pagination = computed(() => request.value.pagination)

  // watch(active, () => {
  //   pagination.value.page = 1
  // })
  return reactive({ loading, data, run, pagination })
}
